C言語メモ


typedef とdefineの違い
   typedefは新しいデータ型の名前(別名)を作成するものです。

(例) typedef int myINT
    は、 myINTと云う同義語をつくり その意味は intです。
   すなわち myINT a;  は int a; と同等に扱われます。


一方、 #define myINT int も
   myINT a;   は   int a;    と同等に扱われます。

 しかし myINT a,b; はエラーとなる。  コンパイラは int a; は理解するが bに関しては解釈不能となる。

 ★★ #defineはプリプロセッサにより解釈され、typedefはコンパイラが解釈する。



typedef による定義
   予約語 typedefの意味
  typedef int myINT;
   と書くと myINTは intの別名(エイリアス)となり
  myINT i = 7, j = 8, k = 9;
   i, j, kは それぞれ7,8,9の整数である。
   
   したがって、

 は、正確に表現すると、『型名 struct RECA{ int x; int y}の構造体の別名型が RECAであることを定義』している。
  上記の定義に於いては構造体のタグ名RECAと別名RECAが同じであるが、定義に於いてタグ名と別名が異なっても
 またタグ名が無くてもよい。  プログラムの実行結果に差異はない。 タグ名は無効であり、使えない
 以下、参照
 

        
#include <stdio.h>

//構造体タグ名RECAは、別名の型名RECAと同じ場合
typedef struct RECA //タグ名
{
    int x;
    int y;
} RECA; //型名


//構造体タグ名なしの場合
typedef struct
{
    int x;
    int y;
} RECB; //型名


//構造体タグ名と別名の型名は異なる場合
typedef struct XYZ //タグ名 //→ タグ名 XYZはプログラムの中では無効であり、使えない。
{
	int x;
	int y;
} RECC; //型名


int main()
{
	RECA reca;	//RECA reca;が無いとreca.x = 1;reca.y = 2;がコンパラエラーとなる
	reca.x = 1;
	reca.y = 2;

	RECB recb;
	recb.x = 3;
	recb.y = 4;

	RECC recc;
	recc.x = 5;
	recc.y = 6;

//	XYX xyz;	//XYZ xyz;はコンパイラエラーとなる

	printf("reca.x=%d, reca.y=%d\n",reca.x, reca.y);
	printf("recb.x=%d, recb.y=%d\n",recb.x,recb.y);
	printf("recc.x=%d, recc.y=%d\n",recc.x,recc.y);

	return 0;
}


      
  <実行結果>
  reca.x=1, reca.y=2
recb.x=3, recb.y=4
recc.x=5, recc.y=6
  (参考使用例:Harmony gfx_driver.h) → URL
   
■ 構造体の定義に typedef を使うメリット 
      1. 構造体を宣言する時(オブジェクトを生成する時) struct を書く手間が省ける。
    2. ソースコードの可読性が向上する。
    3. オブジェクト指向的なソースコードをつくることができる
  <typedefを使用した場合>
 

//Harmony app.h より
typedef struct	//構造体を定義
{
    /* The application's current state */
    APP_STATES state;

    /* TODO: Define any additional data used by the application. */

} APP_DATA;


APP_DATA appData;	//構造体を宣言、オブジェクトを生成

  <typedefを使用しない場合>
 
struct		//構造体を定義
{
    /* The application's current state */
    APP_STATES state;

    /* TODO: Define any additional data used by the application. */

} APP_DATA;


struct APP_DATA appData;	//構造体を宣言、オブジェクトを生成

   
   C++、Javaのクラスとの記述比較
   Cでtypedef を使った記述をすることにより、C++やJavaのようなオブジェクト指向的なコードを書くことができるようになる。
   1. Cのtypedefを使った記述
 
#include<stdio.h>

typedef struct
{
	int x;
	int y;
}REC;

int main()
{
	REC rec;
	rec.x = 1;
	rec.y = 2;

	printf("rec.x=%d\n", rec.x);
	printf("rec.y=%d", rec.y);
}

  <実行結果>
  rec.x=1
rec.y=2
   
   2. C++のクラスを使った記述 
 
#include<iostream>
using namespace std;

class REC{
public:
	int x;
	int y;
};

int main()
{
	REC rec;
	rec.x = 1;
	rec.y = 2;

	cout<<"rec.x = "<<rec.x<<endl;
	cout<<"rec.y = "<<rec.y<<endl;

	return 0;
}


  <実行結果>
  rec.x = 1
rec.y = 2
   
   3. Javaのクラスを使った記述
 
class REC{
	int x;
	int y;
}

public class Java_Variable_xy {
	public static void main(String[] args) {
		
		REC rec = new REC();
		rec.x = 1;
		rec.y = 2;
		
		System.out.println("rec.x = " + rec.x );
		System.out.println("rec.y = " + rec.y );
	}
}

  <実行結果>
  rec.x = 1
rec.y = 2
   
   
  ■ typedef enum
  C言語でオブジェクト指向のプログラミングをする場合、 構造体と同様に、『名前付き整数定数に typedef enum を用いて列挙型の別名を定義する』 ことが行われる。

『列挙型 { APP_STATE_INIT=0,APP_STATE_DISK_SCANNING,APP_STATE_OPEN_FILE,APP_STATE_SEND_DATA,}の別名型が APP_STATES であることを定義』している。
 以下、参照
 
#define _CORE_TIMER_VECTOR 		0
#define _EXTERNAL_0_VECTOR 		3
#define _UART6_RX_VECTOR 		189
#define _ADC7_WARM_VECTOR 		213

#include <stdio.h>

typedef enum
{
	APP_STATE_INIT=0,
	APP_STATE_DISK_SCANNING,
	APP_STATE_OPEN_FILE,
	APP_STATE_SEND_DATA,
} APP_STATES;	//アプリケーションの状態を示す列挙型APP_STATESを定義

typedef enum
{
	COLOR_Red = 0xf800,
	COLOR_Blue = 0x001f,
	COLOR_NavyBlue = 0x0019,
	COLOR_ForestGreen = 0x1c43,
}SYS_COLOR;		//色を示す列挙型SYS_COLORを定義

typedef enum
{
	INT_SOURCE_CORE_TIMER = _CORE_TIMER_VECTOR,
	INT_SOURCE_EXTERNAL_0 = _EXTERNAL_0_VECTOR,
	INT_SOURCE_UART6_RX = _UART6_RX_VECTOR,
	INT_SOURCE_ADC7_WARM = _ADC7_WARM_VECTOR,
} INT_SOURCE;	//割込みベクタ番号の列挙型を定義

typedef struct
{
	APP_STATES state;	//アプリケーションの状態の列挙型メンバー変数、
	SYS_COLOR color;	//色の列挙型メンバー変数
	INT_SOURCE source;	////割込みベクタ番号の列挙型メンバー変数
} APP_DATA;		//アプリケーションのデータを格納する構造体を定義


int main()
{
	APP_DATA appData;	//構造体の変数宣言、オブジェクト生成
	appData.state = APP_STATE_OPEN_FILE;	//メンバー変数、オブジェクトを初期化
	appData.color = COLOR_NavyBlue;
	appData.source = _UART6_RX_VECTOR;

	printf("appData.state = %d, APP_STATE = APP_STATE_OPEN_FILE\n",appData.state);
	printf("appData.color = 0x%04x, SYS_COLOR = COLOR_NavyBlue\n",appData.color);
	printf("appData.source = %d, INT_SOURCE = _UART6_RX_VECTOR\n",appData.source);

	return 0;
}


  <実行結果>
  appData.state = 2, APP_STATE = APP_STATE_OPEN_FILE
appData.color = 0x0019, SYS_COLOR = COLOR_NavyBlue
appData.source = 189, INT_SOURCE = _UART6_RX_VECTOR
   
 uintptr_t
    ・ ポインタと同じサイズの符号なし整数。 
  ・ Cで、ポインター保持に充分な大きさの符号なしの整数型。C99(ISO/IEC 9899:1999)で追加された。(使用例 → URL
   
   
関数ポインタ
   関数ポインタで関数を呼び出す方法
1. typedefを使用しないで関数ポインタを宣言する場合
 1.1 戻り値、引き数がない関数

//Less_typedef_void_F.c
//typedefを使用しないで
// 関数を指すポインタにより、関数を呼び出す方法

#include <stdio.h>

//void (*ptrFunc)();//ここでもコンパイル & 動作可
		    //ptrFuncは『void 型を戻り値とする関数』へのポインタ」である と宣言
		    //ptrFuncの型は『void型へのポインタ型』(void型変数のアドレスを格納する型)

char Buf[32];


void hellow(void)
{
	sprintf(Buf,"Hellow World !!\n");
}

void how(void)
{
	sprintf(Buf,"How are you ?\n");
}

void pen(void)
{
	sprintf(Buf,"This is a pen\n");
}

void boy(void)
{
	sprintf(Buf,"I am a boy\n");
}


int main()
{
	void (*ptrFunc)();	//main()の外にあってもコンパイル & 動作可	//main()の中にある場合が一般的である
				//ptrFuncは『void 型を戻り値とする関数』へのポインタである と宣言
				//ptrFuncの型は『void型へのポインタ型』(void型変数のアドレスを格納する型)
	ptrFunc = hellow;
	ptrFunc();
	printf("%s",Buf);

	ptrFunc = how;
	ptrFunc();
	printf("%s",Buf);

	ptrFunc = pen;
	(*ptrFunc)();
	printf("%s",Buf);


	ptrFunc = boy;
	(*ptrFunc)();
	printf("%s",Buf);

	return 0;
}


<実行結果>
Hellow World !!
How are you ?
This is a pen
I am a boy
 
 1.2 戻り値、引き数がある関数
//Less_typedef_int_F.c
//typedefを使用しないで
// 関数を指すポインタにより、関数を呼び出す方法

#include <stdio.h>

//int (*ptrFunc)(int, int);	//main()の中も可	//main()の中が一般的
				//ptrFuncは『int 型を戻り値とする関数』へのポインタであると宣言
				//ptrFuncの型は『int型へのポインタ型』(int型変数のアドレスを格納する型)
int add(int x, int y)
{
	return x + y;
}

int multiply(int x, int y)
{
	return x * y;
}


int main()
{
	int (*ptrFunc)(int, int);	//main()の外であっても可	//main()の中が一般的
					//ptrFuncは『int 型を戻り値とする関数』へのポインタであると宣言
					//ptrFuncの型は『int型へのポインタ型』(int型変数のアドレスを格納する型)

	ptrFunc = add;
	printf("add(4,5) = %d\n", ptrFunc(4,5));

	ptrFunc = multiply;
	printf("multiply(4,5) = %d\n", ptrFunc(4,5));

	ptrFunc = add;
	printf("add(6,7) = %d\n", (*ptrFunc)(6,7));

	ptrFunc = multiply;
	printf("multiply(6,7) = %d\n", (*ptrFunc)(6,7));

//以下はコンパイラNGコード
//	int Func(int, int);
//
//	Func* func;
//
//	func = add;
//	printf("add(4,5) = %d\n", func(4,5));
//
//	func = multiply;
//	printf("multiply(4,5) = %d\n", func(4,5));
//
//	func = add;
//	printf("add(6,7) = %d\n", (*func)(6,7));
//
//	func = multiply;
//	printf("multiply(6,7) = %d\n", (*func)(6,7));

       return 0;
}



<実行結果>
add(4,5) = 9
multiply(4,5) = 20
add(6,7) = 13
multiply(6,7) = 42
 
 
2. typedef を使用して関数ポインタを宣言する場合
 2.1 戻り値の型の別名を 関数名として宣言する場合
  2.1.1 戻り値、引き数がない関数
//typedef_void_F.c
//typedefを使用して
// 関数を指すポインタにより、関数を呼び出す方法(戻り値がvoid型の場合)

#include <stdio.h>

typedef void Func(void);	// Funcは『void 型を戻り値とする関数』であると宣言、
                                // Funcは void型の別名であり、関数名でもあると宣言
char Buf[32];

void hellow(void)
{
	sprintf(Buf,"Hellow World !!\n");
}

void how(void)
{
	sprintf(Buf,"How are you ?\n");
}


int main()
{
//	typedef void Func(void); //main()の中にあってもOK // Funcは『void 型を戻り値とする関数』であると宣言、

	Func* func;		//funcは Funcを指すポインタであると宣言

	func = hellow;
	func();
	printf("%s",Buf);

	func = how;
	func();
	printf("%s",Buf);

	return 0;
}



<実行結果>
Hellow World !!
How are you ?
 
 
  2.1.2 戻り値と引き数がある関数

//typedef_int_F.c
//typedefを使用して
// 関数を指すポインタにより、関数を呼び出す方法(戻り値がint型の場合)

#include <stdio.h>

typedef int Func(int, int);	// Funcは『int 型を戻り値とする関数』であると宣言、
                                // Funcは int型の別名であり、関数名でもあると宣言
int add(int x, int y)
{
	return x + y;
}

int multiply(int x, int y)
{
	return x * y;
}

int main()
{
	//typedef int Func(int, int);	//main()の中にあってもOK // Funcは『int 型を戻り値とする関数』であると宣言

	Func *func;	//Funcを指すポインタの変数funcを生成

    func = add;
    printf("add(4,5) = %d\n", func(4,5));

    func = multiply;
    printf("multiply(4,5) = %d\n", func(4,5));
    
    return 0;

}

<実行結果>
add(4,5) = 9
multiply(4,5) = 20
 
 
 2.2 明示的に関数ポインタを宣言する場合
  2.2.1 戻り値、引き数がない関数
//typedef_void_ptrF.c
//typedefを使用して
// 関数を指すポインタにより、関数を呼び出す方法(戻り値が void型の場合)

#include <stdio.h>

typedef void (*ptrFunc)(void);	//「ptrFuncは『void 型を戻り値とする関数』へのポインタ」を宣言
					//ptrFuncの型は『void型へのポインタ型』(void型変数のアドレスを格納する型)

char Buf[32];


void hellow(void)
{
	sprintf(Buf,"Hellow World !!\n");
}

void how(void)
{
	sprintf(Buf,"How are you ?\n");
}


int main()
{
//	typedef void (*ptrFunc)(void);	//main()の中にあってもOK

	ptrFunc func;	//ptrFuncの変数funcを生成
			//→『void 型を戻り値とする関数』へのポインタの変数funcを生成

	func = hellow;
	func();
	printf("%s",Buf);

	func = how;
	func();
	printf("%s",Buf);

	return 0;
}



<実行結果>
Hellow World !!
How are you ?
 
 
  2.2.2 戻り値と引き数がある関数
//typedef_int_ptrF.c
//typedefを使用して
// 関数を指すポインタにより、関数を呼び出す方法(戻り値がint型の場合)

#include <stdio.h>

typedef int (*ptrFunc)(int, int); //ptrFuncは『int 型を戻り値とする関数』へのポインタであると宣言
				      //ptrFuncの型は『int型へのポインタ型』(int型変数のアドレスを格納する型)

int add(int x, int y)
{
	return x + y;
}

int multiply(int x, int y)
{
	return x * y;
}

int main()
{
//	typedef int (*ptrFunc)(int, int);		//main()の中にあってもOK

	ptrFunc func;		//ptrFuncの変数funcを生成
				//→『int 型を戻り値とする関数』へのポインタの変数funcを生成

    func = add;
    printf("add(2,3) = %d\n", func(2,3));

    func = multiply;
    printf("multiply(2,3) = %d\n", func(2,3));

   return 0;
}



<実行結果>
add(2,3) = 5
multiply(2,3) = 6
 
   (参考使用例: Harmony  sys_time_definitions.h) → URL
   
   
  ■ 関数ポインタメモ
   
関数へのポインタをメンバーにもつ構造体
  ・ C言語では構造体の中に関数をメンバーとしてもつことはできないが、関数ポインタを利用すると 関数へのポインタをメンバー変数をもつ構造体をつくることができる。これを使って、同じ呼び出しで異なる関数の呼び出しができる。 この手法はHarmonyの中でよく用いられている。
//10 interface_struct1.c
//同じ呼び出しで異なる関数を呼び出す

#include <stdio.h>

typedef int (*ADD)(int x,int y);	//ADDはintを戻り値とする関数名(ポインタ)であると宣言
typedef int (*MUL)(int x,int y);	//MULはintを戻り値とする関数名(ポインタ)であると宣言


typedef struct
{
	ADD add;	//関数名ADDの変数addを宣言
	MUL mul;	//関数名MULの変数mulを宣言

}FUNC_INTERFACE;	//関数へのポインタをメンバーとする構造体を宣言

typedef struct
{
	int x;	//整数の変数xを宣言
	int y;	//整数の変数yを宣言

	FUNC_INTERFACE func_interface;	//関数へのポインタをメンバーとする構造体への変数を宣言

}CALC;	//関数へのポインタをメンバー変数にもつ構造体を宣言



int add1(int x, int y)
{
	int ans;

	ans = x + y + 1;
	return ans;
}

int add2(int x, int y)
{
	int ans;

	ans = x + y + 2;
	return ans;
}

int multiply1(int x, int y)
{
	int ans;

	ans = x * y;
	return ans;
}

int multiply2(int x, int y)
{
	int ans;

	ans = x * y * 2;
	return ans;
}


int main()
{
	int value;
	CALC calc;

	FUNC_INTERFACE func_interface = 	//構造体初期化
	{
		.add = (ADD)add1,			//関数の実体を引き当て
		.mul = (MUL)multiply1,		//関数の実体を引き当て
	};


	value = (*func_interface.add)(1,2);	//加算演算実行
	printf("value = %d\n", value);
	value = (*func_interface.mul)(1,2);	//乗算演算実行
	printf("value = %d\n", value);

	calc.x = 3;		//CALC構造体のxを初期化
	calc.y = 4;		//CALC構造体のyを初期化
	value = (*func_interface.add)(calc.x,calc.y);
	printf("value = %d\n", value);
	value = (*func_interface.mul)(calc.x,calc.y);
	printf("value = %d\n", value);


	//関数変更	//同じ呼び出しで異なる関数を呼び出す
	func_interface.add = (ADD)add2;
	func_interface.mul = (MUL)multiply2;

	value = (*func_interface.add)(1,2);
	printf("\nvalue = %d\n", value);
	value = (*func_interface.mul)(1,2);
	printf("value = %d\n", value);

	calc.x = 3;
	calc.y = 4;
	value = (*func_interface.add)(calc.x,calc.y);
	printf("value = %d\n", value);
	value = (*func_interface.mul)(calc.x,calc.y);
	printf("value = %d\n", value);
}

  <実行結果>
  value = 4
value = 2
value = 8
value = 12

value = 5
value = 4
value = 9
value = 24